package net.sansi.v3correctserver;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import lombok.extern.slf4j.Slf4j;
import net.sansi.v3correctserver.dto.R;
import net.sansi.v3correctserver.exception.ServiceException;
import net.sansi.v3correctserver.exception.ValidateException;
import net.sansi.v3correctserver.util.I18nUtil;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.RejectedExecutionException;

@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 拦截在Model上添加了 @Validated 注解的方法
     * 失败后会被拦截到这里
     * 并且抛出一个带有验证错误信息的异常
     *
     * @param ex 异常
     * @return R
     */
    @ExceptionHandler(value = BindException.class)
    public R<Object> validatorException(BindException ex) {
        Map<String, Object> rMap = new HashMap<>();
        for (FieldError error : ex.getBindingResult().getFieldErrors()) {
            rMap.put(error.getField(), error.getDefaultMessage());
        }
        log.error("表单验证失败 validatorException: {}", rMap, ex);
        return R.error()
                .setValidateFailed(rMap)
                .setMessage(I18nUtil.get("validation.form.error"))
                .setCode(R.VALIDATE_FAILED);
    }

    /**
     * 业务层手动抛出的 ValidateException 异常
     *
     * @param ex 异常
     * @return R
     */
    @ExceptionHandler(value = ValidateException.class)
    public R<Object> ValidateFailException(ValidateException ex) {
        log.error("表单验证失败 ValidateFailException: {}", ex.getValidateFailed(), ex);
        return R.error()
                .setValidateFailed(ex.getValidateFailed())
                .setMessage(I18nUtil.get("validation.form.error"))
                .setCode(R.VALIDATE_FAILED);
    }


    /**
     * 在Controller层使用 @Validated 注解的方法
     *
     * @param ex 异常
     * @return R
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    public R<Object> constraintViolationException(ConstraintViolationException ex) {
        Map<String, Object> errors = new HashMap<>();
        for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
            errors.put(violation.getPropertyPath().toString(), violation.getMessage());
        }
        log.error("表单验证失败 constraintViolationException: {}", errors, ex);
        return R.error()
                .setValidateFailed(errors)
                .setMessage(I18nUtil.get("validation.form.error"))
                .setCode(R.VALIDATE_FAILED);
    }

    /**
     * 业务层手动抛出的 ServiceException 异常
     *
     * @param ex 异常
     * @return R
     */
    @ExceptionHandler(value = ServiceException.class)
    public R<Object> serviceException(ServiceException ex) {
        log.error("ServiceException : {}", ex.getMessage(), ex);
        return R.error()
                .setMessage(ex.getMessage())
                .setCode(ex.getCode());
    }

    /**
     * 用户未登录异常
     *
     * @param ex 异常
     * @return R
     */
    @ExceptionHandler(value = NotLoginException.class)
    public R<Object> notLoginException(NotLoginException ex) {
        log.error("未登录异常 notLoginException: {}", ex.getMessage(), ex);
        return R.error()
                .setMessage("not login")
                .setCode(R.NOT_LOGIN);
    }

    /**
     * 无权限异常
     *
     * @param ex 异常
     * @return R
     */
    @ExceptionHandler(value = NotPermissionException.class)
    public R<Object> notPermissionException(NotPermissionException ex) {
        log.error("无权限异常 notPermissionException: {}", ex.getMessage(), ex);
        return R.error()
                .setMessage("not permission")
                .setCode(R.NO_PERMISSION);
    }

    /**
     * 线程池拒绝异常
     *
     * @param ex 异常
     * @return R
     */
    @ExceptionHandler(value = RejectedExecutionException.class)
    public R<Object> exception(RejectedExecutionException ex) {
        log.error("线程池拒绝异常 exception: {}", ex.getMessage(), ex);
        return R.error()
                .setMessage("thread pool reject")
                .setCode(R.ERROR);
    }

    /**
     * 其他异常
     *
     * @param ex 异常
     * @return R
     */
    @ExceptionHandler(value = Exception.class)
    public R<Object> exception(Exception ex) {
        log.error("其他异常 exception: {}", ex.getMessage(), ex);
        return R.error()
                .setMessage(ex.getMessage())
                .setCode(R.ERROR);
    }

}